package com.zlc.workflow.core.instance.cmd;

import com.zlc.common.core.process.mapper.StoneProcessInstanceHistoryMapper;
import com.zlc.common.core.process.model.vo.*;
import com.zlc.common.core.process.service.*;
import com.zlc.common.core.user.service.MayanPlatUserService;
import com.zlc.common.model.user.UserInfo;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.BeanCopyUtils;
import com.zlc.common.util.SnowflakeUtils;
import com.zlc.common.util.UserHelper;
import com.zlc.workflow.constant.TaskActionEnum;
import com.zlc.workflow.constant.WorkflowStatusEnum;
import com.zlc.workflow.core.instance.model.QueryFlowDetailParam;
import com.zlc.workflow.core.instance.model.WorkflowAction;
import com.zlc.workflow.core.instance.model.WorkflowDetail;
import com.zlc.workflow.core.instance.model.WorkflowTask;
import com.zlc.workflow.core.process.model.ProcessFormConfig;
import com.zlc.workflow.flow.NodeUtils;
import com.zlc.workflow.flow.node.TaskNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.zlc.common.common.constant.WorkflowConstant.INSTANCE_STATUS_DRAFT;
import static com.zlc.common.common.constant.WorkflowConstant.INSTANCE_STATUS_PROCESSING;

@Service
public class QueryWorkflowDetailCmd {

    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessInstanceService stoneProcessInstanceService;
    @Autowired
    private StoneProcessInstanceHistoryService stoneProcessInstanceHistoryService;
    @Autowired
    private StoneProcessAuthConfigService stoneProcessAuthConfigService;
    @Autowired
    private StoneProcessFormConfigService stoneProcessFormConfigService;
    @Autowired
    private StoneTaskService stoneTaskService;
    @Autowired
    private StoneTaskHistoryService stoneTaskHistoryService;
    @Autowired
    private StoneProcessNodeService stoneProcessNodeService;
    @Autowired
    private MayanPlatUserService mayanPlatUserService;
    @Autowired
    private StoneProcessVariableConfigService stoneProcessVariableConfigService;
    @Autowired
    private StoneProcessInstanceHistoryMapper stoneProcessInstanceHistoryMapper;

    public WorkflowDetail getWorkflowDetail(QueryFlowDetailParam param) {
        String processId = param.getProcessId();
        String processInstanceId = param.getProcessInstanceId();
        if (ObjectUtils.isEmpty(processInstanceId)) {
            //流程实例ID不传，查询流程开始节点信息
            return getStartDetail(processId, param.getAccount());
        }
        return getTaskDetail(processInstanceId, param.getTaskId(), param.getProcessId(), param.getAccount());
    }

    private WorkflowDetail getTaskDetail(String processInstanceId, String taskId, String processId, String account) {
        WorkflowDetail detail = new WorkflowDetail();
        detail.setProcessInstanceId(processInstanceId);
        StoneProcessVo processVo = stoneProcessService.getByProcessId(processId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processVo), "无效的流程-" + processId);
        //流程实例
        StoneProcessInstanceVo instanceVo = stoneProcessInstanceService.getByProcessInstanceId(processInstanceId);
        if(ObjectUtils.isEmpty(instanceVo)){
            StoneProcessInstanceHistoryVo histInstanceVo = stoneProcessInstanceHistoryService.getByProcessInstanceId(processInstanceId);
            AssertUtils.isTrue(!ObjectUtils.isEmpty(histInstanceVo), "无效的流程实例-" + processInstanceId);
            instanceVo = BeanCopyUtils.copyAndInit(histInstanceVo, StoneProcessInstanceVo.class);
        }

        detail.setStatus(instanceVo.getStatus());
        detail.setStatusName(WorkflowStatusEnum.getStatusName(instanceVo.getStatus()));
        //任务实例
        if(!ObjectUtils.isEmpty(taskId)){
            StoneTaskVo taskVo = stoneTaskService.getByTaskId(taskId);
            if(!ObjectUtils.isEmpty(taskVo)){
                buildWorkflowDetailByActiveTask(detail,processVo,instanceVo,taskVo);
            }else{
                StoneTaskHistoryVo historyTask = stoneTaskHistoryService.getByTaskId(taskId);
                AssertUtils.isTrue(!ObjectUtils.isEmpty(historyTask), "无效的任务-" + taskId);
                buildWorkflowDetailByHistTask(detail,processVo,instanceVo,historyTask);
            }
        }else{
            buildWorkflowDetailByHistTask(detail,processVo,instanceVo,null);
        }
        return detail;
    }

    private void buildWorkflowDetailByHistTask(WorkflowDetail detail,StoneProcessVo processVo, StoneProcessInstanceVo instanceVo, StoneTaskHistoryVo taskVo){
        List<StoneTaskVo> taskVos = stoneTaskService.getByProcessInstanceId(instanceVo.getProcessInstanceId());
        String nowTaskNames = taskVos.stream().map(StoneTaskVo::getNodeName).distinct().collect(Collectors.joining(","));
        WorkflowTask task = BeanCopyUtils.copyAndInit(instanceVo, WorkflowTask.class);
        task.setNodeName(nowTaskNames);
        task.setInitAccount(instanceVo.getStarter());
        task.setInitUserName(instanceVo.getStarterName());
        task.setProcessCode(processVo.getProcessCode());
        task.setProcessName(processVo.getProcessName());
        detail.setDetails(task);
        if(ObjectUtils.isEmpty(taskVo)){
            return;
        }
        task.setReceiveTime(taskVo.getReceiveTime());
        task.setTaskStatus(taskVo.getStatus());
        //权限
        List<String> auths = queryNodeAuth(processVo.getProcessId(), taskVo.getNodeId());
        detail.setAuths(auths);
        //表单
        detail.setNodeFormConfigs(new ArrayList<>());
        List<StoneProcessFormConfigVo> nodeForm = stoneProcessFormConfigService.getNodeForm(processVo.getProcessId(), taskVo.getNodeId());
        if (!ObjectUtils.isEmpty(nodeForm)) {
            List<ProcessFormConfig> formConfigs = BeanCopyUtils.copyObjList(nodeForm, ProcessFormConfig.class);
            detail.setNodeFormConfigs(formConfigs);
        }
        //变量
        Map<String, String> variableConfigs = getVariableConfigs(processVo.getProcessId(), taskVo.getNodeId());
        detail.setVariableConfigs(variableConfigs);
        //操作
        detail.setActions(new ArrayList<>());
    }

    private void buildWorkflowDetailByActiveTask(WorkflowDetail detail,StoneProcessVo processVo, StoneProcessInstanceVo instanceVo, StoneTaskVo taskVo){
        WorkflowTask task = BeanCopyUtils.copyAndInit(instanceVo, WorkflowTask.class);
        task.setTaskId(taskVo.getTaskId());
        task.setNodeId(taskVo.getTaskId());
        //节点信息
        StoneProcessNodeVo nodeVo = stoneProcessNodeService.getByNodeId(processVo.getProcessId(), taskVo.getNodeId());
        task.setNodeName(nodeVo.getNodeName());
        task.setNodeNum(nodeVo.getNodeNum());
        task.setInitAccount(instanceVo.getStarter());
        task.setInitUserName(instanceVo.getStarterName());
        task.setCurUserName(taskVo.getActorName());
        task.setCurAccount(taskVo.getActorId());
        task.setProcessCode(processVo.getProcessCode());
        task.setProcessName(processVo.getProcessName());
        task.setReceiveTime(taskVo.getReceiveTime());
        task.setTaskStatus(taskVo.getStatus());
        detail.setDetails(task);
        //权限
        List<String> auths = queryNodeAuth(processVo.getProcessId(), taskVo.getNodeId());
        detail.setAuths(auths);
        //表单
        detail.setNodeFormConfigs(new ArrayList<>());
        List<StoneProcessFormConfigVo> nodeForm = stoneProcessFormConfigService.getNodeForm(processVo.getProcessId(), taskVo.getNodeId());
        if (!ObjectUtils.isEmpty(nodeForm)) {
            List<ProcessFormConfig> formConfigs = BeanCopyUtils.copyObjList(nodeForm, ProcessFormConfig.class);
            detail.setNodeFormConfigs(formConfigs);
        }
        //变量
        Map<String, String> variableConfigs = getVariableConfigs(processVo.getProcessId(), taskVo.getNodeId());
        detail.setVariableConfigs(variableConfigs);
        //操作
        List<WorkflowAction> taskAction = getTaskAction(instanceVo, taskVo, processVo.getProcessId());
        detail.setActions(taskAction);
    }

    private List<WorkflowAction> getTaskAction(StoneProcessInstanceVo instanceVo, StoneTaskVo taskVo, String processId) {
        UserInfo nowUser = UserHelper.getNowUser();
        if (!taskVo.getActorId().equals(nowUser.getUserAccount())) {
            return new ArrayList<>();
        }
        List<WorkflowAction> actions = new ArrayList<>();
        //第一个任务
        TaskNode firstTask = NodeUtils.getFirstTask(processId);
        StoneProcessNodeVo nodeVo = stoneProcessNodeService.getByNodeId(processId, taskVo.getNodeId());
        if (Arrays.asList(INSTANCE_STATUS_PROCESSING, INSTANCE_STATUS_DRAFT).contains(instanceVo.getStatus())) {
            //下一步
            actions.add(new WorkflowAction(TaskActionEnum.SUBMIT));
        }
        if (canReject(instanceVo, nodeVo, taskVo, firstTask)) {
            actions.add(new WorkflowAction(TaskActionEnum.REJECT));
        }
        if (INSTANCE_STATUS_DRAFT.equals(instanceVo.getStatus()) || (firstTask != null && firstTask.getNodeId().equals(taskVo.getNodeId()))) {
            actions.add(new WorkflowAction(TaskActionEnum.SAVE));
        }
        if (INSTANCE_STATUS_PROCESSING.equals(instanceVo.getStatus())) {
            actions.add(new WorkflowAction(TaskActionEnum.TERMINATION));
        }
        if (INSTANCE_STATUS_PROCESSING.equals(instanceVo.getStatus()) && firstTask != null &&
                !taskVo.getNodeId().equals(firstTask.getNodeId())) {
            actions.add(new WorkflowAction(TaskActionEnum.TRANSFER));
            actions.add(new WorkflowAction(TaskActionEnum.ADD_ACTOR));
        }
        return actions;
    }

    private boolean canReject(StoneProcessInstanceVo instanceVo, StoneProcessNodeVo nodeVo, StoneTaskVo taskVo, TaskNode firstTask) {
        if (!INSTANCE_STATUS_PROCESSING.equals(instanceVo.getStatus())) {
            return false;
        }
        if (firstTask != null && taskVo.getNodeId().equals(firstTask.getNodeId())) {
            //第一个任务节点,不可退回
            return false;
        }
        if (nodeVo != null && !Boolean.TRUE.equals(nodeVo.getRejectFlag())) {
            return false;
        }
        return true;
    }


    private WorkflowDetail getStartDetail(String processId, String account) {
        WorkflowDetail detail = new WorkflowDetail();
        detail.setProcessInstanceId(SnowflakeUtils.next());

        StoneProcessVo processVo = stoneProcessService.getByProcessId(processId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processVo), "无效的流程-" + processId);
        String content = NodeUtils.getProcessContent(processVo.getContent());
        //第一个任务节点
        TaskNode firstTask = NodeUtils.getFirstTask(content, processId);
        //权限
        List<String> auths = queryNodeAuth(processId, firstTask.getNodeId());
        detail.setAuths(auths);
        //表单
        List<StoneProcessFormConfigVo> nodeForm = stoneProcessFormConfigService.getNodeForm(processId, firstTask.nodeId());
        if (!ObjectUtils.isEmpty(nodeForm)) {
            List<ProcessFormConfig> formConfigs = BeanCopyUtils.copyObjList(nodeForm, ProcessFormConfig.class);
            detail.setNodeFormConfigs(formConfigs);
        }
        //任务
        WorkflowTask task = new WorkflowTask();
        task.setProcessInstanceId(detail.getProcessInstanceId());
        task.setNodeId(firstTask.getNodeId());
        task.setNodeName(firstTask.getNodeName());
        task.setNodeNum(firstTask.getNodeNum());
        task.setProcessCode(processVo.getProcessCode());
        task.setProcessName(processVo.getProcessName());

        //用户
        String userName = mayanPlatUserService.queryUserName(account);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(userName), "无效的用户-" + account);
        task.setCurAccount(account);
        task.setCurUserName(userName);
        task.setSystem(processVo.getSystem());
        task.setModule(processVo.getModule());

        detail.setDetails(task);
        return detail;
    }

    private List<String> queryNodeAuth(String processId, String nodeId) {
        List<StoneProcessAuthConfigVo> nodeAuth = stoneProcessAuthConfigService.getNodeAllowAuth(processId, nodeId);
        List<StoneProcessAuthConfigVo> processAllowAuth = stoneProcessAuthConfigService.getProcessAllowAuth(processId);
        if (!ObjectUtils.isEmpty(processAllowAuth)) {
            nodeAuth.addAll(processAllowAuth);
        }
        return nodeAuth.stream().filter(s -> Boolean.TRUE.equals(s.getAllowFlag()))
                .map(StoneProcessAuthConfigVo::getAuthRes).distinct().collect(Collectors.toList());
    }


    private Map<String, String> getVariableConfigs(String processId, String nodeId) {
        return stoneProcessVariableConfigService.getVariableConfigs(processId, nodeId);
    }

}
